package mips.instructions;

/**
 * <code>ADDIU</code> instruction<br/>
 * Add Immediate Unsigned<br/>
 * @author jnmartin84@gmail.com
 */
public class ADDIU extends Instruction {

	private static final ADDIU INSTANCE = new ADDIU();
	private static final String INSTRUCTION_NAME = "ADDIU";

	private ADDIU(){}

	public static final ADDIU getInstance() {
		return INSTANCE;
	}

	/**
	 * <b>Format:</b><br/>
	 * ADDIU rt, rs, immediate<br/><br/>
	 * <b>Description:</b><br/>
	 * The 16-bit immediate is sign-extended and added to the contents of general<br/>
	 * register rs to form the result. The result is placed into general register rt.<br/>
	 * No integer overflow exception occurs under any circumstances.<br/> 
	 * The only difference between this instruction and the ADDI instruction is<br/>
	 * that ADDIU never causes an overflow exception.<br/><br/>
	 * <b>Operation:</b><br/>
	 * T: GPR[rt] &larr; GPR[rs] + (immediate<sub>15</sub>)<sup>16</sup> || immediate<sub>15...0</sub>
	 */
	@Override
	public final void execute(final int instruction) {

		mips.instructions.Instruction.RS = (instruction >> 21) & 0x0000001F;
		mips.instructions.Instruction.RT = (instruction >> 16) & 0x0000001F;
		mips.instructions.Instruction.IMMED = mips.instructions.Instruction.signExtendH(instruction & 0x0000FFFF);

		final long u32_gpr_rs = Instruction.zeroExtendW(mips.R4300i.GPR[mips.instructions.Instruction.RS]); 

		// cast 64-bit result back down to 32-bit, ignoring overflow
		mips.R4300i.GPR[mips.instructions.Instruction.RT] = (int)((u32_gpr_rs + mips.instructions.Instruction.IMMED) & mips.Constants.MAX_U32_AS_S64);

		mips.R4300i.PC = mips.R4300i.nPC;
		mips.R4300i.nPC = mips.R4300i.PC + 4;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public final String emit(int instruction) {

		mips.instructions.Instruction.RS = (instruction >> 21) & 0x0000001F;
		mips.instructions.Instruction.RT = (instruction >> 16) & 0x0000001F;
		mips.instructions.Instruction.IMMED = mips.instructions.Instruction.signExtendH(instruction & 0x0000FFFF);
		
		return	"		u32_gpr_rs = mips.instructions.Instruction.zeroExtendW(mips.CPU.GPR["+mips.instructions.Instruction.RS+"]); \n" + 
				"		\n" + 
				"		mips.CPU.GPR["+mips.instructions.Instruction.RT+"] = (int)((u32_gpr_rs + "+mips.instructions.Instruction.IMMED+") & "+mips.Constants.MAX_U32_AS_S64+"L);\n" + 
				"		\n" + 
				"		mips.CPU.PC = mips.CPU.nPC;\n" + 
				"		mips.CPU.nPC = mips.CPU.PC + 4;\n";
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public final String getName(final int instruction) {
		return getName();
	}
	
	/**
	 * {@inheritDoc}
	 */
	@Override
	public final String getName() {
		return INSTRUCTION_NAME;
	}
}